package com.cskaoyan.javase.recursion._0introduction;

/**
 * 在一个故事当中，再次提到了同样的故事
 * 那么这种类似 "套娃"的理念，其实就是递归（recursion）的核心理念。
 *
 * 递归的定义
 * 我们已经学习过方法，使用过方法了。
 * 方法体中是可以调用方法的(同一个类中,所有static修饰的方法,都可以直接用方法名互相调用)
 * 那么如果在方法体中调用方法自身呢？
 *
 * 特别的，在一个方法当中再次调用这个方法，就像故事里提到同样的故事一样
 * 我们把方法在运行时调用自身的情况，称之为递归，又叫做递归调用。
 *
 * 方法的递归,使用的注意事项:
 *      1.要想使用递归,首先需要在方法当中存在自身调用自身方法的语句,称之为"递归的递归体"
 *          递归体是不可或缺的.
 *      2.要想递归能够正常执行结束,而不是栈溢出,需要明确指出递归调用方法的停止条件
 *          称之为"递归的出口"
 *          一个合法的,正常的递归,递归的出口也是不可或缺的.
 *
 *      3.一个递归,有递归体,有递归的出口,那么它就一定能够正常执行吗?
 *      当然不是的,如果递归的出口过于遥远,导致在方法的递归调用的过程中,栈内存的空间不够用了,一样会栈溢出
 *      所以在使用递归时,还需要考虑递归的次数(递归的深度),不能超过栈内存的大小
 *
 *  综上所述,递归是一个非常危险的操作,稍有不慎都有可能产生栈溢出
 *  所以实际开发中,递归是非常少见使用的语法,而且也不建议大家使用,能不用就不要.
 *
 * @since 14:57
 * @author wuguidong@cskaoyan.onaliyun.com
 */
public class Demo {
    public static void main(String[] args) {
        /*
        没有递归出口之前:
            分析一下以下方法的调用结果:
            sum(10) = 10 + sum(9);
            sum(9) = 9 + sum(8);
            sum(8) = 8 + sum(7);
            ...
            显然以上过程是不可能自己停止的
            那么程序的输出结果是什么呢?
                1.死循环
                2.报错
                3.电脑受不了,电脑炸了(这个是肯定不可能的)

            实际上程序执行的结果是: 报错终止,并且在控制台打印了异常信息,也就是说程序产生了异常
            异常是: StackOverflowError(栈溢出错误)
                栈溢出: 哪个栈溢出? JVM栈
                Error: Error是比Exception更加严重的异常,是错误.

            那么程序的运行为什么会栈溢出呢?
            因为方法一直在调用,方法的栈帧一直进栈
            但始终没有方法执行完毕,始终没有方法栈帧出栈
            栈的空间是有限的,所以栈内存终究会不够用,会栈溢出

            又如何避免出错呢?
            之所以栈溢出是因为方法栈帧只有进,没有出
            所以只需要给一个条件,让方法结束,出栈,就可以了

         */
        // System.out.println(sum(100));
        sum(16000);
    }

    // 递归求和
    public static long sum(int num) {
        // 递归的出口
        if (num == 0){
            return 0;
        }
        return num + sum(num - 1);
    }
}
